home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltGrLegd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-20  |  28.5 KB  |  926 lines

  1.  
  2. /*
  3.  * bltGrLegd.c --
  4.  *
  5.  *    This module implements legends for the graph widget for
  6.  *    the Tk toolkit.
  7.  *
  8.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  9.  * Permission to use, copy, modify, and distribute this software
  10.  * and its documentation for any purpose and without fee is hereby
  11.  * granted, provided that the above copyright notice appear in all
  12.  * copies and that both that the copyright notice and warranty
  13.  * disclaimer appear in supporting documentation, and that the
  14.  * names of AT&T Bell Laboratories any of their entities not be used
  15.  * in advertising or publicity pertaining to distribution of the
  16.  * software without specific, written prior permission.
  17.  *
  18.  * AT&T disclaims all warranties with regard to this software, including
  19.  * all implied warranties of merchantability and fitness.  In no event
  20.  * shall AT&T be liable for any special, indirect or consequential
  21.  * damages or any damages whatsoever resulting from loss of use, data
  22.  * or profits, whether in an action of contract, negligence or other
  23.  * tortuous action, arising out of or in connection with the use or
  24.  * performance of this software.
  25.  *
  26.  */
  27.  
  28. #include "blt.h"
  29. #include "bltGraph.h"
  30. #include "bltGrElem.h"
  31.  
  32. #define DEF_LEGEND_ACTIVE_BG_COLOR     BG2
  33. #define DEF_LEGEND_ACTIVE_BG_MONO    WHITE
  34. #define DEF_LEGEND_ACTIVE_BORDER_WIDTH  "2"
  35. #define DEF_LEGEND_ACTIVE_FG_COLOR    BLACK
  36. #define DEF_LEGEND_ACTIVE_FG_MONO    BLACK
  37. #define DEF_LEGEND_ACTIVE_RELIEF    "flat"
  38. #define DEF_LEGEND_ANCHOR           "nw"
  39. #define DEF_LEGEND_BG_COLOR           BG2
  40. #define DEF_LEGEND_BG_MONO        WHITE
  41. #define DEF_LEGEND_BORDER_WIDTH     "2"
  42. #define DEF_LEGEND_FG_COLOR        BLACK
  43. #define DEF_LEGEND_FG_MONO        BLACK
  44. #define DEF_LEGEND_FONT            "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
  45. #define DEF_LEGEND_IPAD_X        "1"
  46. #define DEF_LEGEND_IPAD_Y        "1"
  47. #define DEF_LEGEND_MAPPED           "1"
  48. #define DEF_LEGEND_PAD_X        "4"
  49. #define DEF_LEGEND_PAD_Y        "0"
  50. #define DEF_LEGEND_POSITION        (char *)NULL
  51. #define DEF_LEGEND_RELIEF        "sunken"
  52.  
  53. extern Tk_CustomOption bltPositionOption;
  54.  
  55. /*
  56.  * -------------------------------------------------------------------
  57.  *
  58.  * Legend --
  59.  *
  60.  *     Contains information specific to how the legend will be
  61.  *    displayed.
  62.  *
  63.  * -------------------------------------------------------------------
  64.  */
  65.  
  66.  
  67. typedef struct {
  68.     int mapped;            /* Requested state of the legend, If non-zero,
  69.                  * legend is displayed */
  70.     unsigned int width, height;    /* Dimensions of the legend */
  71.     XPoint anchorPos;        /* Window coordinates of legend positioning
  72.                  * point. Used in conjunction with the anchor
  73.                  * to determine the location of the legend. If
  74.                  * x or y are DEF_POSITION the legend is set
  75.                  * in the right margin */
  76.     int useDefault;        /* Use the default legend position */
  77.  
  78.     LegendDisplayProc *displayProc;
  79.     LegendPrintProc *printProc;
  80.     LegendDestroyProc *destroyProc;
  81.     LegendGeometryProc *geomProc;
  82.  
  83.     int ipadX, ipadY;        /* # of pixels padding around legend entries */
  84.     int padX, padY;        /* # of pixels padding to exterior of legend */
  85.     unsigned int numLabels;    /* Number of labels (and symbols) to display */
  86.     unsigned int numCols;    /* Number of columns in legend */
  87.     unsigned int numRows;    /* Number of rows in legend */
  88.     unsigned int entryWidth;
  89.     unsigned int entryHeight;
  90.  
  91.     int maxSymSize;        /* Size of largest symbol to be displayed.
  92.                  * Used to calculate size of legend */
  93.     Tk_Anchor anchor;        /* Anchor of legend. Used to interpret the
  94.                  * positioning point of the legend in the
  95.                  * graph*/
  96.     XFontStruct *fontPtr;    /* Font for legend text */
  97.  
  98.     Tk_3DBorder border;        /* 3-D border and background color legend. */
  99.     int borderWidth;        /* Width of legend 3-D border */
  100.     int relief;            /* 3-d effect of border around the legend:
  101.                  * TK_RELIEF_RAISED etc. */
  102.     XColor *normalFg;        /* Foreground color for legend
  103.                  * text. Symbols retain the color
  104.                  * specified for the element*/
  105.     GC normalGC;        /* Normal legend entry graphics context */
  106.  
  107.     Tk_3DBorder activeBorder;    /* Background color for active legend
  108.                  * entries. */
  109.     int activeBW;        /* Width of legend 3-D border */
  110.     int activeRelief;        /* 3-d effect: TK_RELIEF_RAISED etc. */
  111.     XColor *activeFg;        /* Foreground color for active legend
  112.                  * entries. Symbols retain the color
  113.                  * specified for the element*/
  114.     GC activeGC;        /* Active legend entry graphics context */
  115.  
  116. } Legend;
  117.  
  118. static Tk_ConfigSpec configSpecs[] =
  119. {
  120.     {TK_CONFIG_BORDER, "-activebackground", "legendActiveBackground",
  121.     "ActiveBackground", DEF_LEGEND_ACTIVE_BG_COLOR,
  122.     Tk_Offset(Legend, activeBorder), TK_CONFIG_COLOR_ONLY},
  123.     {TK_CONFIG_BORDER, "-activebackground", "legendActiveBackground",
  124.     "ActiveBackground", DEF_LEGEND_ACTIVE_BG_MONO,
  125.     Tk_Offset(Legend, activeBorder), TK_CONFIG_MONO_ONLY},
  126.     {TK_CONFIG_INT, "-activeborderwidth", "legendActiveBorderWidth",
  127.     "BorderWidth", DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, activeBW),
  128.     TK_CONFIG_DONT_SET_DEFAULT},
  129.     {TK_CONFIG_COLOR, "-activeforeground", "legendActiveForeground",
  130.     "ActiveForeground", DEF_LEGEND_ACTIVE_FG_COLOR,
  131.     Tk_Offset(Legend, activeFg), TK_CONFIG_COLOR_ONLY},
  132.     {TK_CONFIG_COLOR, "-activeforeground", "legendActiveForeground",
  133.     "ActiveForeground", DEF_LEGEND_ACTIVE_FG_MONO,
  134.     Tk_Offset(Legend, activeFg), TK_CONFIG_MONO_ONLY},
  135.     {TK_CONFIG_RELIEF, "-activerelief", "legendActiveRelief", "Relief",
  136.     DEF_LEGEND_ACTIVE_RELIEF, Tk_Offset(Legend, activeRelief),
  137.     TK_CONFIG_DONT_SET_DEFAULT},
  138.     {TK_CONFIG_ANCHOR, "-anchor", "legendAnchor", "Anchor",
  139.     DEF_LEGEND_ANCHOR, Tk_Offset(Legend, anchor),
  140.     TK_CONFIG_DONT_SET_DEFAULT},
  141.     {TK_CONFIG_SYNONYM, "-bg", "legendBackground", (char *)NULL,
  142.     (char *)NULL, 0, 0},
  143.     {TK_CONFIG_BORDER, "-background", "legendBackground", "Background",
  144.     DEF_LEGEND_BG_MONO, Tk_Offset(Legend, border),
  145.     TK_CONFIG_MONO_ONLY},
  146.     {TK_CONFIG_BORDER, "-background", "legendBackground", "Background",
  147.     DEF_LEGEND_BG_COLOR, Tk_Offset(Legend, border),
  148.     TK_CONFIG_COLOR_ONLY},
  149.     {TK_CONFIG_INT, "-borderwidth", "legendBorderWidth", "BorderWidth",
  150.     DEF_LEGEND_BORDER_WIDTH, Tk_Offset(Legend, borderWidth),
  151.     TK_CONFIG_DONT_SET_DEFAULT},
  152.     {TK_CONFIG_SYNONYM, "-bd", "legendBorderWidth", (char *)NULL,
  153.     (char *)NULL, 0, 0},
  154.     {TK_CONFIG_FONT, "-font", "legendFont", "Font",
  155.     DEF_LEGEND_FONT, Tk_Offset(Legend, fontPtr), 0},
  156.     {TK_CONFIG_SYNONYM, "-fg", "legendForeground", (char *)NULL, (char *)NULL,
  157.     0, 0},
  158.     {TK_CONFIG_COLOR, "-foreground", "legendForeground", "Foreground",
  159.     DEF_LEGEND_FG_COLOR, Tk_Offset(Legend, normalFg),
  160.     TK_CONFIG_COLOR_ONLY},
  161.     {TK_CONFIG_COLOR, "-foreground", "legendForeground", "Foreground",
  162.     DEF_LEGEND_FG_MONO, Tk_Offset(Legend, normalFg),
  163.     TK_CONFIG_MONO_ONLY},
  164.     {TK_CONFIG_PIXELS, "-ipadx", "legendIPadX", "Pad",
  165.     DEF_LEGEND_IPAD_X, Tk_Offset(Legend, ipadX),
  166.     TK_CONFIG_DONT_SET_DEFAULT},
  167.     {TK_CONFIG_PIXELS, "-ipady", "legendIPadY", "Pad",
  168.     DEF_LEGEND_IPAD_Y, Tk_Offset(Legend, ipadY),
  169.     TK_CONFIG_DONT_SET_DEFAULT},
  170.     {TK_CONFIG_BOOLEAN, "-mapped", "legendMapped", "Mapped",
  171.     DEF_LEGEND_MAPPED, Tk_Offset(Legend, mapped),
  172.     TK_CONFIG_DONT_SET_DEFAULT},
  173.     {TK_CONFIG_PIXELS, "-padx", "legendPadX", "Pad",
  174.     DEF_LEGEND_PAD_X, Tk_Offset(Legend, padX), TK_CONFIG_DONT_SET_DEFAULT},
  175.     {TK_CONFIG_PIXELS, "-pady", "legendPadY", "Pad",
  176.     DEF_LEGEND_PAD_Y, Tk_Offset(Legend, padY), TK_CONFIG_DONT_SET_DEFAULT},
  177.     {TK_CONFIG_CUSTOM, "-position", "legendPosition", "Position",
  178.     DEF_LEGEND_POSITION, Tk_Offset(Legend, anchorPos),
  179.     TK_CONFIG_NULL_OK, &bltPositionOption},
  180.     {TK_CONFIG_RELIEF, "-relief", "legendRelief", "Relief",
  181.     DEF_LEGEND_RELIEF, Tk_Offset(Legend, relief),
  182.     TK_CONFIG_DONT_SET_DEFAULT},
  183.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  184. };
  185.  
  186. extern int Blt_GetPosition _ANSI_ARGS_((Tcl_Interp *interp, char *string,
  187.     XPoint *pointPtr));
  188.  
  189. static XPoint
  190. GetOrigin(graphPtr, legendPtr)
  191.     Graph *graphPtr;
  192.     Legend *legendPtr;
  193. {
  194.     int x, y;
  195.     Tk_Anchor anchor;
  196.     XPoint origin;
  197.  
  198.     x = (graphPtr->width - graphPtr->borderWidth);
  199.     y = (graphPtr->extreme.y - graphPtr->plotBW);
  200.     anchor = TK_ANCHOR_NE;
  201.     if (!legendPtr->useDefault) {
  202.     x = legendPtr->anchorPos.x;
  203.     y = legendPtr->anchorPos.y;
  204.     anchor = legendPtr->anchor;
  205.     }
  206.     origin = Blt_TranslateBoxCoords(x, y, legendPtr->width, legendPtr->height,
  207.     anchor);
  208.     origin.x += legendPtr->padX;
  209.     origin.y += legendPtr->padY;
  210.     return (origin);
  211. }
  212.  
  213.  
  214.  
  215. static int
  216. GetIndex(legendPtr, originPtr, pointPtr)
  217.     Legend *legendPtr;
  218.     XPoint *originPtr, *pointPtr;
  219. {
  220.     int x, y;
  221.     unsigned int width, height;
  222.     unsigned int row, column;
  223.     unsigned int index;
  224.  
  225.     x = originPtr->x + legendPtr->borderWidth;
  226.     y = originPtr->y + legendPtr->borderWidth;
  227.     width = legendPtr->width - 2 * (legendPtr->padX + legendPtr->borderWidth);
  228.     height = legendPtr->height - 2 * (legendPtr->padY + legendPtr->borderWidth);
  229.  
  230.     if ((pointPtr->x < x) || (pointPtr->x > (x + width)) ||
  231.     (pointPtr->y < y) || (pointPtr->y > (y + height))) {
  232.     return -1;
  233.     }
  234.     /* It's in the box. Compute the label index */
  235.  
  236.     row = (pointPtr->y - y) / legendPtr->entryHeight;
  237.     column = (pointPtr->x - x) / legendPtr->entryWidth;
  238.     index = (column * legendPtr->numRows) + row;
  239.     if (index >= legendPtr->numLabels) {
  240.     return -1;
  241.     }
  242.     return index;
  243. }
  244.  
  245.  
  246.  
  247. static Element *
  248. LocateElement(graphPtr, legendPtr, elemId)
  249.     Graph *graphPtr;
  250.     Legend *legendPtr;
  251.     char *elemId;
  252. {
  253.     Blt_ListEntry *entryPtr;
  254.     Element *elemPtr;
  255.  
  256.     elemPtr = NULL;
  257.     if (elemId[0] == '@') {
  258.     XPoint origin, point;
  259.     int count, index;
  260.  
  261.     if (Blt_GetPosition(graphPtr->interp, elemId, &point) != TCL_OK) {
  262.         return NULL;
  263.     }
  264.     origin = GetOrigin(graphPtr, legendPtr);
  265.     index = GetIndex(legendPtr, &origin, &point);
  266.     if (index < 0) {
  267.         return NULL;
  268.     }
  269.     count = 0;
  270.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  271.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  272.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  273.         if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  274.         (elemPtr->label == NULL)) {
  275.         continue;
  276.         }
  277.         if (count == index) {
  278.         break;
  279.         }
  280.         count++;
  281.     }
  282.     }
  283.     return (elemPtr);
  284. }
  285.  
  286. /*
  287.  * -----------------------------------------------------------------
  288.  *
  289.  * ComputeLegendGeometry --
  290.  *
  291.  *     Calculates the dimensions (width and height) needed for
  292.  *    the legend.  Also determines the number of rows and columns
  293.  *    necessary to list all the valid element labels.
  294.  *
  295.  * Results:
  296.  *      None.
  297.  *
  298.  * Side effects:
  299.  *       The following fields of the legend are calculated and set.
  300.  *
  301.  *     numLabels   - number of valid labels of elements in the
  302.  *              display list.
  303.  *     numRows        - number of rows of entries
  304.  *     numCols        - number of columns of entries
  305.  *     entryHeight - height of each entry
  306.  *     entryWidth  - width of each entry
  307.  *     height        - width of legend (includes borders and padding)
  308.  *     width        - height of legend (includes borders and padding)
  309.  *
  310.  * -----------------------------------------------------------------
  311.  */
  312. static void
  313. ComputeLegendGeometry(graphPtr, maxHeight)
  314.     Graph *graphPtr;
  315.     int maxHeight;
  316. {
  317.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  318.     unsigned int width, height, twiceBW;
  319.     unsigned int numLabels, numRows, numCols;
  320.  
  321.     numCols = numRows = numLabels = 0;
  322.     height = width = 0;
  323.     if (legendPtr->mapped) {
  324.     register Blt_ListEntry *entryPtr;
  325.     Element *elemPtr;
  326.     unsigned int textHeight, entryHeight, entryWidth;
  327.     unsigned int w;
  328.  
  329.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  330.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  331.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  332.         if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  333.         (elemPtr->label == NULL)) {
  334.         continue;    /* Ignore elements with incomplete data */
  335.         }
  336.         w = Blt_TextStringWidth(legendPtr->fontPtr, elemPtr->label);
  337.         if (w > width) {
  338.         width = w;
  339.         }
  340.         numLabels++;
  341.     }
  342.     textHeight = TEXTHEIGHT(legendPtr->fontPtr);
  343.     twiceBW = (2 * legendPtr->activeBW);
  344.     entryHeight = textHeight + twiceBW + (2 * legendPtr->ipadY);
  345.     entryWidth = width + textHeight + twiceBW + (3 * legendPtr->ipadX);
  346.     maxHeight -= 2 * (legendPtr->borderWidth + legendPtr->padY);
  347.     numRows = maxHeight / entryHeight;
  348.     if (numRows > 0) {
  349.         numCols = ((numLabels - 1) / numRows) + 1;
  350.         if (numRows > numLabels) {
  351.         numRows = numLabels;
  352.         }
  353.         height = (2 * (legendPtr->borderWidth + legendPtr->padY)) +
  354.         (numRows * entryHeight);
  355.         width = (2 * (legendPtr->borderWidth + legendPtr->padX)) +
  356.         (numCols * entryWidth);
  357.     }
  358.     legendPtr->entryWidth = entryWidth;
  359.     legendPtr->entryHeight = entryHeight;
  360.     legendPtr->numRows = numRows;
  361.     legendPtr->numCols = numCols;
  362.     }
  363.     legendPtr->numLabels = numLabels;
  364.     legendPtr->height = height;
  365.     legendPtr->width = width;
  366. }
  367.  
  368. /*
  369.  * -----------------------------------------------------------------
  370.  *
  371.  * DisplayLegend --
  372.  *
  373.  * -----------------------------------------------------------------
  374.  */
  375. static void
  376. DisplayLegend(graphPtr)
  377.     Graph *graphPtr;
  378. {
  379.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  380.     int x, y, startY;
  381.     register Element *elemPtr;
  382.     unsigned int width, height;
  383.     unsigned int labelX, symbolX, symbolY;
  384.     unsigned int counter;
  385.     Blt_ListEntry *entryPtr;
  386.     XPoint origPos, curPos;
  387.     unsigned int symSize, midPoint;
  388.     TextAttributes textAttr;
  389.     int redraw;
  390.  
  391.     graphPtr->flags &= ~LEGEND_ONLY;
  392.     if ((!legendPtr->mapped) || (legendPtr->numLabels == 0) ||
  393.     (legendPtr->numRows == 0) || (legendPtr->numCols == 0)) {
  394.     return;
  395.     }
  396.     width = legendPtr->width - (2 * legendPtr->padX);
  397.     height = legendPtr->height - (2 * legendPtr->padY);
  398.     symSize = legendPtr->fontPtr->ascent;
  399.     midPoint = (symSize / 2) + 1 + legendPtr->activeBW;
  400.  
  401.     labelX = TEXTHEIGHT(legendPtr->fontPtr) + legendPtr->activeBW +
  402.     (2 * legendPtr->ipadX);
  403.     symbolY = midPoint + legendPtr->ipadY;
  404.     symbolX = midPoint + legendPtr->ipadX;
  405.  
  406.     origPos = GetOrigin(graphPtr, legendPtr);
  407.     x = origPos.x, y = origPos.y, redraw = 0;
  408.  
  409.     if (graphPtr->canvas == None) {
  410.     /*
  411.      * If there's no pixmap already to draw into, then this
  412.      * routine was called from Tk_DoWhenIdle instead of
  413.      * DisplayGraph.  Create a temporary pixmap and reset the x,y
  414.      * coordinates to do a quick redisplay of just the legend.
  415.      */
  416.     graphPtr->canvas = XCreatePixmap(graphPtr->display,
  417.         Tk_WindowId(graphPtr->tkwin), width, height,
  418.         Tk_Depth(graphPtr->tkwin));
  419.     x = y = 0;
  420.     redraw = 1;
  421.     }
  422.     Tk_Fill3DRectangle(graphPtr->tkwin, graphPtr->canvas,
  423.     legendPtr->border, x, y, width, height, legendPtr->borderWidth,
  424.     legendPtr->relief);
  425.     x += legendPtr->borderWidth;
  426.     y += legendPtr->borderWidth;
  427.  
  428.     textAttr.fontPtr = legendPtr->fontPtr;
  429.     textAttr.anchor = TK_ANCHOR_W;
  430.     textAttr.theta = 0.0;
  431.     textAttr.bgColorPtr = (XColor *)NULL;
  432.     textAttr.gc = legendPtr->normalGC;
  433.  
  434.     counter = 0;
  435.     startY = y;
  436.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  437.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  438.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  439.  
  440.     /*
  441.      * Draw each symbol and label.  Check that the label isn't NULL and
  442.      * that the element has at least one data point.
  443.      */
  444.  
  445.     if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  446.         (elemPtr->label == NULL)) {
  447.         continue;
  448.     }
  449.     textAttr.gc = legendPtr->normalGC;
  450.  
  451.     if (elemPtr->flags & LABEL_ACTIVE) {
  452.         Tk_Fill3DRectangle(graphPtr->tkwin, graphPtr->canvas,
  453.         legendPtr->activeBorder, x, y, legendPtr->entryWidth,
  454.         legendPtr->entryHeight, legendPtr->activeBW,
  455.         legendPtr->activeRelief);
  456.         textAttr.gc = legendPtr->activeGC;
  457.     }
  458.     curPos.x = x + symbolX;
  459.     curPos.y = y + symbolY;
  460.     (*elemPtr->drawSymbolsProc) (graphPtr, elemPtr, symSize, &curPos, 1,
  461.         ELEM_NORMAL);
  462.     curPos.x = x + labelX;
  463.     Blt_DrawText(graphPtr->display, graphPtr->canvas, elemPtr->label,
  464.         &textAttr, curPos.x, curPos.y);
  465.     counter++;
  466.  
  467.     /* Check when to move to the next column */
  468.     if ((counter % legendPtr->numRows) > 0) {
  469.         y += legendPtr->entryHeight;
  470.     } else {
  471.         x += legendPtr->entryWidth;
  472.         y = startY;
  473.     }
  474.     }
  475.     if (redraw) {
  476.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  477.     XCopyArea(graphPtr->display, graphPtr->canvas,
  478.         Tk_WindowId(graphPtr->tkwin), graphPtr->marginGC, 0, 0,
  479.         width, height, origPos.x, origPos.y);
  480.     (*graphPtr->crosshairs->toggleProc) (graphPtr);
  481.     XFreePixmap(graphPtr->display, graphPtr->canvas);
  482.     graphPtr->canvas = None;
  483.     }
  484. }
  485.  
  486. /*
  487.  * -----------------------------------------------------------------
  488.  *
  489.  * PrintLegend --
  490.  *
  491.  * -----------------------------------------------------------------
  492.  */
  493. static void
  494. PrintLegend(graphPtr)
  495.     Graph *graphPtr;
  496. {
  497.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  498.     int x, y, startY;
  499.     register Element *elemPtr;
  500.     unsigned int labelX, symbolX, symbolY;
  501.     unsigned int counter;
  502.     Blt_ListEntry *entryPtr;
  503.     XPoint origPos, curPos;
  504.     Tk_Anchor anchor;        /* Anchor of legend */
  505.     unsigned int symSize, midPoint;
  506.     int width, height;
  507.     TextAttributes textAttr;
  508.  
  509.     if ((!legendPtr->mapped) || (legendPtr->numLabels == 0) ||
  510.     (legendPtr->numRows == 0) || (legendPtr->numCols == 0)) {
  511.     return;
  512.     }
  513.     /*
  514.      * First calculate the upper left coordinate of the legend.
  515.      * Consider the anchor.
  516.      */
  517.  
  518.     x = graphPtr->width - graphPtr->borderWidth;
  519.     y = graphPtr->extreme.y - graphPtr->plotBW;
  520.     anchor = TK_ANCHOR_NE;
  521.  
  522.     if (!legendPtr->useDefault) {
  523.     double scale, coord;
  524.     /*
  525.      * Legend position was given in window coordinates so we have to
  526.      * scale using the current page coordinates.
  527.      */
  528.     scale = (double)graphPtr->width / Tk_Width(graphPtr->tkwin);
  529.     coord = (legendPtr->anchorPos.x * scale);
  530.     x = BLT_RND(coord);
  531.     scale = (double)graphPtr->height / Tk_Height(graphPtr->tkwin);
  532.     coord = (legendPtr->anchorPos.y * scale);
  533.     y = BLT_RND(coord);
  534.     anchor = legendPtr->anchor;
  535.     }
  536.     origPos = Blt_TranslateBoxCoords(x, y, legendPtr->width,
  537.     legendPtr->height, anchor);
  538.  
  539.     x = origPos.x + legendPtr->padX;
  540.     y = origPos.y + legendPtr->padY;
  541.  
  542.     width = legendPtr->width - (2 * legendPtr->padX);
  543.     height = legendPtr->height - (2 * legendPtr->padY);
  544.     Blt_3DRectangleToPostScript(graphPtr, legendPtr->border, x, y, width,
  545.     height, legendPtr->borderWidth, legendPtr->relief);
  546.  
  547.     x += legendPtr->borderWidth;
  548.     y += legendPtr->borderWidth;
  549.  
  550.     symSize = legendPtr->fontPtr->ascent;
  551.     midPoint = (symSize / 2) + 1 + legendPtr->activeBW;
  552.  
  553.     labelX = TEXTHEIGHT(legendPtr->fontPtr) + legendPtr->activeBW +
  554.     (2 * legendPtr->ipadX);
  555.     symbolY = midPoint + legendPtr->ipadY;
  556.     symbolX = midPoint + legendPtr->ipadX;
  557.  
  558.     textAttr.fontPtr = legendPtr->fontPtr;
  559.     textAttr.anchor = TK_ANCHOR_W;
  560.     textAttr.theta = 0.0;
  561.     textAttr.fgColorPtr = graphPtr->marginFg;
  562.     textAttr.bgColorPtr = (XColor *)NULL;
  563.  
  564.     counter = 0;
  565.     startY = y;
  566.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  567.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  568.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  569.  
  570.     /*
  571.      * Print each symbol and label.  Check that the label isn't NULL and
  572.      * that the element has at least one data point.
  573.      */
  574.     if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1) ||
  575.         (elemPtr->label == NULL)) {
  576.         continue;
  577.     }
  578.     textAttr.fgColorPtr = legendPtr->normalFg;
  579.     if (elemPtr->flags & LABEL_ACTIVE) {
  580.         Blt_3DRectangleToPostScript(graphPtr, legendPtr->activeBorder,
  581.         x, y, legendPtr->entryWidth, legendPtr->entryHeight,
  582.         legendPtr->activeBW, legendPtr->activeRelief);
  583.         textAttr.fgColorPtr = legendPtr->activeFg;
  584.     }
  585.     curPos.x = x + symbolX;
  586.     curPos.y = y + symbolY;
  587.     (*elemPtr->printSymbolsProc) (graphPtr, elemPtr, symSize, &curPos, 1,
  588.         ELEM_NORMAL);
  589.     curPos.x = x + labelX;
  590.     Blt_TextToPostScript(graphPtr, elemPtr->label, &textAttr, curPos.x,
  591.         curPos.y);
  592.     counter++;
  593.     if ((counter % legendPtr->numRows) > 0) {
  594.         y += legendPtr->entryHeight;
  595.     } else {
  596.         x += legendPtr->entryWidth;
  597.         y = startY;
  598.     }
  599.     }
  600. }
  601.  
  602. /*
  603.  *----------------------------------------------------------------------
  604.  *
  605.  * ConfigureLegend --
  606.  *
  607.  *     Routine to configure the legend.
  608.  *
  609.  * Results:
  610.  *    The return value is a standard Tcl result.
  611.  *
  612.  * Side Effects:
  613.  *    Graph will be redrawn to reflect the new legend attributes.
  614.  *
  615.  *----------------------------------------------------------------------
  616.  */
  617. static int
  618. ConfigureLegend(graphPtr, legendPtr, argc, argv, flags)
  619.     Graph *graphPtr;
  620.     Legend *legendPtr;
  621.     int argc;
  622.     char *argv[];
  623.     int flags;
  624. {
  625.     GC newGC;
  626.     XGCValues gcValues;
  627.     unsigned long gcMask;
  628.     int newFlag;
  629.  
  630.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin,
  631.         configSpecs, argc, argv, (char *)legendPtr, flags) != TCL_OK) {
  632.     return TCL_ERROR;
  633.     }
  634.     gcMask = GCForeground | GCFont;
  635.     gcValues.font = legendPtr->fontPtr->fid;
  636.     gcValues.foreground = legendPtr->normalFg->pixel;
  637.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  638.     if (legendPtr->normalGC != NULL) {
  639.     Tk_FreeGC(graphPtr->display, legendPtr->normalGC);
  640.     }
  641.     legendPtr->normalGC = newGC;
  642.  
  643.     gcMask |= GCBackground;
  644.     gcValues.foreground = legendPtr->activeFg->pixel;
  645.     gcValues.background = Tk_3DBorderColor(legendPtr->activeBorder)->pixel;
  646.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  647.     if (legendPtr->activeGC != NULL) {
  648.     Tk_FreeGC(graphPtr->display, legendPtr->activeGC);
  649.     }
  650.     legendPtr->activeGC = newGC;
  651.     newFlag = (legendPtr->anchorPos.x == DEF_POSITION);
  652.  
  653.     /*
  654.      *
  655.      *  Update the layout of the graph (and redraw the elements) if
  656.      *  any of the following legend options which affect the size of
  657.      *    the legend changed.
  658.      *
  659.      *        -activeborderwidth, -borderwidth
  660.      *        -font
  661.      *        -mapped
  662.      *        -ipadx, -ipady, -padx, -pady
  663.      *
  664.      *  If the position of the legend changed to/from the default
  665.      *  position, also indicate that a new layout is needed.
  666.      *
  667.      */
  668.     if ((legendPtr->useDefault != newFlag) || ((legendPtr->useDefault) &&
  669.         (Blt_OptionChanged(configSpecs, "-*borderwidth", "-*pad?",
  670.             "-mapped", "-font", (char *)NULL)))) {
  671.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  672.     legendPtr->useDefault = newFlag;
  673.     }
  674.     graphPtr->flags |= REFRESH;
  675.     Blt_RedrawGraph(graphPtr);
  676.     return TCL_OK;
  677. }
  678.  
  679. /*
  680.  *----------------------------------------------------------------------
  681.  *
  682.  * DestroyLegend --
  683.  *
  684.  * Results:
  685.  *    None.
  686.  *
  687.  * Side effects:
  688.  *    Resources associated with the legend are freed.
  689.  *
  690.  *----------------------------------------------------------------------
  691.  */
  692. static void
  693. DestroyLegend(graphPtr)
  694.     Graph *graphPtr;
  695. {
  696.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  697.  
  698.     Tk_FreeOptions(configSpecs, (char *)legendPtr, graphPtr->display, 0);
  699.     if (legendPtr->normalGC != NULL) {
  700.     Tk_FreeGC(graphPtr->display, legendPtr->normalGC);
  701.     }
  702.     if (legendPtr->activeGC != NULL) {
  703.     Tk_FreeGC(graphPtr->display, legendPtr->activeGC);
  704.     }
  705.     free((char *)legendPtr);
  706. }
  707.  
  708. /*
  709.  *----------------------------------------------------------------------
  710.  *
  711.  * Blt_CreateLegend --
  712.  *
  713.  *     Creates and initializes a legend structure with default settings
  714.  *
  715.  * Results:
  716.  *    The return value is a standard Tcl result.
  717.  *
  718.  *----------------------------------------------------------------------
  719.  */
  720. /*ARGSUSED*/
  721. int
  722. Blt_CreateLegend(graphPtr)
  723.     Graph *graphPtr;
  724. {
  725.     Legend *legendPtr;
  726.  
  727.     legendPtr = (Legend *)calloc(1, sizeof(Legend));
  728.     if (legendPtr == NULL) {
  729.     graphPtr->interp->result = "can't allocate legend structure";
  730.     return TCL_ERROR;
  731.     }
  732.     legendPtr->mapped = TRUE;
  733.     legendPtr->anchorPos.x = legendPtr->anchorPos.y = DEF_POSITION;
  734.     legendPtr->useDefault = 1;
  735.     legendPtr->relief = TK_RELIEF_SUNKEN;
  736.     legendPtr->activeRelief = TK_RELIEF_FLAT;
  737.     legendPtr->activeBW = legendPtr->borderWidth = 2;
  738.     legendPtr->ipadX = legendPtr->ipadY = 1;
  739.     legendPtr->padX = 4;
  740.     legendPtr->padY = 0;
  741.     legendPtr->anchor = TK_ANCHOR_NW;
  742.     legendPtr->displayProc = DisplayLegend;
  743.     legendPtr->printProc = PrintLegend;
  744.     legendPtr->destroyProc = DestroyLegend;
  745.     legendPtr->geomProc = ComputeLegendGeometry;
  746.     graphPtr->legendPtr = (GraphLegend *)legendPtr;
  747.     return (ConfigureLegend(graphPtr, legendPtr, 0, (char **)NULL, 0));
  748. }
  749.  
  750. /*
  751.  *----------------------------------------------------------------------
  752.  *
  753.  * GetEntry --
  754.  *
  755.  *     Find the legend entry from the given argument.  The argument
  756.  *    can be either a screen position "@x,y" or the name of an
  757.  *    element.
  758.  *
  759.  *    I don't know how useful it is to test with the name of an
  760.  *    element.
  761.  *
  762.  * Results:
  763.  *    The return value is a standard Tcl result.
  764.  *
  765.  * Side Effects:
  766.  *    Graph will be redrawn to reflect the new legend attributes.
  767.  *
  768.  *----------------------------------------------------------------------
  769.  */
  770. static int
  771. GetEntry(graphPtr, legendPtr, argc, argv)
  772.     Graph *graphPtr;
  773.     Legend *legendPtr;
  774.     int argc;
  775.     char *argv[];
  776. {
  777.     register Element *elemPtr;
  778.  
  779.     if ((!legendPtr->mapped) || (legendPtr->numLabels == 0)) {
  780.     return TCL_OK;
  781.     }
  782.     if (argc != 4) {
  783.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  784.         argv[0], " legend search index\"", NULL);
  785.     return TCL_ERROR;
  786.     }
  787.     elemPtr = LocateElement(graphPtr, legendPtr, argv[3]);
  788.     if (elemPtr != NULL) {
  789.     Tcl_SetResult(graphPtr->interp, elemPtr->id, TCL_STATIC);
  790.     }
  791.     return TCL_OK;
  792. }
  793.  
  794. /*
  795.  *----------------------------------------------------------------------
  796.  *
  797.  * ActivateEntry --
  798.  *
  799.  *     Routine to configure the legend.
  800.  *
  801.  * Results:
  802.  *    The return value is a standard Tcl result.
  803.  *
  804.  * Side Effects:
  805.  *    Graph will be redrawn to reflect the new legend attributes.
  806.  *
  807.  *----------------------------------------------------------------------
  808.  */
  809. static int
  810. ActivateEntry(graphPtr, legendPtr, argc, argv)
  811.     Graph *graphPtr;
  812.     Legend *legendPtr;
  813.     int argc;
  814.     char *argv[];
  815. {
  816.     Element *elemPtr;
  817.     Tcl_HashEntry *entryPtr;
  818.     int active;
  819.     int redrawNeeded;
  820.     register int i;
  821.  
  822.     if (argc < 4) {
  823.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  824.         argv[0], " legend ", argv[2], " name...\"", NULL);
  825.     return TCL_ERROR;
  826.     }
  827.     active = (argv[2][0] == 'a') ? LABEL_ACTIVE : 0;
  828.     redrawNeeded = 0;
  829.     for (i = 3; i < argc; i++) {
  830.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[i]);
  831.     if (entryPtr == NULL) {
  832.         Tcl_AppendResult(graphPtr->interp, "can't find an element \"",
  833.         argv[i], "\"", (char *)NULL);
  834.         return TCL_ERROR;
  835.     }
  836.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  837.     if (active != (elemPtr->flags & LABEL_ACTIVE)) {
  838.         elemPtr->flags ^= LABEL_ACTIVE;
  839.         if (elemPtr->label != NULL) {
  840.         redrawNeeded++;
  841.         }
  842.     }
  843.     }
  844.     if ((redrawNeeded) && (legendPtr->mapped)) {
  845.     /*
  846.      * We need to redraw the legend. If there isn't a redraw
  847.      * already pending for the whole graph, we can redraw just the
  848.      * legend, calling the legend's display routine rather than
  849.      * the graph's.  The window must be mapped though.
  850.      */
  851.     if (graphPtr->flags & REDRAW_PENDING) {
  852.         graphPtr->flags |= REFRESH;
  853.     } else if (!(graphPtr->flags & LEGEND_ONLY)) {
  854.         if ((graphPtr->tkwin != NULL) && (Tk_IsMapped(graphPtr->tkwin))) {
  855.         Tk_DoWhenIdle((Tk_IdleProc *) DisplayLegend,
  856.             (ClientData)graphPtr);
  857.         graphPtr->flags |= LEGEND_ONLY;
  858.         }
  859.     }
  860.     }
  861.     return TCL_OK;
  862. }
  863.  
  864. /*
  865.  *----------------------------------------------------------------------
  866.  *
  867.  * Blt_LegendCmd --
  868.  *
  869.  * Results:
  870.  *    The return value is a standard Tcl result.
  871.  *
  872.  * Side Effects:
  873.  *    Legend is possibly redrawn.
  874.  *
  875.  *----------------------------------------------------------------------
  876.  */
  877. /* ARGSUSED */
  878. int
  879. Blt_LegendCmd(graphPtr, argc, argv)
  880.     Graph *graphPtr;
  881.     int argc;
  882.     char **argv;
  883. {
  884.     char c;
  885.     int length;
  886.     Tcl_Interp *interp = graphPtr->interp;
  887.     Legend *legendPtr = (Legend *)graphPtr->legendPtr;
  888.  
  889.     /* Initialize the crosshairs on first call */
  890.  
  891.     if (argc < 3) {
  892.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  893.         " legend option ?args?\"", NULL);
  894.     return TCL_ERROR;
  895.     }
  896.     c = argv[2][0];
  897.     length = strlen(argv[2]);
  898.     if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)) {
  899.     int flags = TK_CONFIG_ARGV_ONLY;
  900.  
  901.     if (argc == 3) {
  902.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  903.             configSpecs, (char *)legendPtr, (char *)NULL, flags));
  904.     } else if (argc == 4) {
  905.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  906.             configSpecs, (char *)legendPtr, argv[3], flags));
  907.     }
  908.     if (ConfigureLegend(graphPtr, legendPtr, argc - 3,
  909.         argv + 3, flags) != TCL_OK) {
  910.         return TCL_ERROR;
  911.     }
  912.     } else if ((c == 'g') && (strncmp(argv[2], "get", length) == 0)) {
  913.     return (GetEntry(graphPtr, legendPtr, argc, argv));
  914.     } else if ((c == 'a') && (strncmp(argv[2], "activate", length) == 0)) {
  915.     return (ActivateEntry(graphPtr, legendPtr, argc, argv));
  916.     } else if ((c == 'd') && (strncmp(argv[2], "deactivate", length) == 0)) {
  917.     return (ActivateEntry(graphPtr, legendPtr, argc, argv));
  918.     } else {
  919.     Tcl_AppendResult(interp, "bad legend option \"", argv[2],
  920.         "\": should be activate, configure, deacitvate, or get",
  921.         (char *)NULL);
  922.     return TCL_ERROR;
  923.     }
  924.     return TCL_OK;
  925. }
  926.